package com.link.base.base.file.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.link.base.base.file.model.HeadRowStructure;
import com.link.core.basic.dao.KeyGenerateDao;
import com.link.core.basic.service.BasicServiceException;
import com.link.core.cllog.LCLogger;
import com.link.core.util.BeanTools;
import com.link.core.util.CoreYmlConstants;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 头行结构导入导出service
 *
 * @author LiYang
 * @date 20191025
 */
@Service
public class ExpAndImpServiceImpl implements ExpAndImpService {

    /**
     * 导出方法
     *
     * @author LiYang
     * @date 20191025
     * @param entity
     * @param request
     * @return
     * @throws Exception
     */
    @Override
    public String exportData(HeadRowStructure entity, HttpServletRequest request) {
        //拼接头请求参数信息
        HttpEntity<Object> headRequestParam = repackageExpHeadParams(entity, request);
        RestTemplate restTemplate = new RestTemplate();
        //得到头请求结果
        Map<String, Object> headResp = restTemplate.postForObject(entity.getHeadUrl(), headRequestParam, Map.class);
        //得到头结果集，这里需要注意的是写死了取“rows”，如果返回参数类型不为Map且结果集不在“rows”中，则无法获得结果集
        List<Map<String, Object>> headList = (List) headResp.get("rows");
        //遍历查出的头结果集
        for (int i = 0; i < headList.size(); i++) {
            Map<String, Object> head = headList.get(i);
            //组装行查询请求条件
            HttpEntity<Object> rowRequestParam = repackageExpRowParams(entity, request, head);
            //得到行查询请求结果
            Map<String, Object> rowsResp = restTemplate.postForObject(entity.getRowUrl(), rowRequestParam, Map.class);
            List<Map<String, Object>> rowList = (List) rowsResp.get("rows");
            //将结果添加到exportChildren属性中
            headList.get(i).put("exportChildren", rowList);
        }
        //将完整的头行结构数据写入文件并返回url。这里需要注意，所有数据都是一次性查出来封装，如果数据量非常大可能会存在内存溢出等问题
        String urlPath = writeExportFile(headList);
        System.out.println(111);
        return urlPath;
    }

    /**
     * 导出方法---拼接头请求参数
     *
     * @author LiYang
     * @date 20191025
     * @param entity
     * @param request
     * @return
     */
    private HttpEntity<Object> repackageExpHeadParams(HeadRowStructure entity, HttpServletRequest request) {
        //拼接请求体
        LinkedMultiValueMap<String, Object> linkedMultiValueMap = new LinkedMultiValueMap<>(16);
        JSONObject param = JSONObject.parseObject(entity.getHeadParams());
        Set<String> keySet = param.keySet();
        Iterator<String> keySetIter = keySet.iterator();
        while (keySetIter.hasNext()) {
            String key = keySetIter.next();
            linkedMultiValueMap.add(key, param.get(key));
        }
        //拼接请求头
        HttpHeaders headers = new HttpHeaders();
        String auth = request.getHeader("Authorization");
        auth = auth.replace("bearer ", "");
        headers.setBearerAuth(auth);
        HttpEntity<Object> result = new HttpEntity<>(linkedMultiValueMap, headers);
        return result;
    }

    /**
     * 导出方法---拼接行请求参数
     *
     * @author LiYang
     * @date 20191025
     * @param entity
     * @param request
     * @param head
     * @return
     */
    private HttpEntity<Object> repackageExpRowParams(HeadRowStructure entity, HttpServletRequest request, Map<String, Object> head) {
        //拼接请求体
        LinkedMultiValueMap<String, Object> linkedMultiValueMap = new LinkedMultiValueMap<>(16);
        //得到行请求的全部参数，转换为jsonobject
        JSONObject param = JSONObject.parseObject(entity.getRowParams());
        //得到请求参数
        String filtersRaw = param.getString("filtersRaw");
        JSONArray jsonFilter = JSONArray.parseArray(filtersRaw);
        for (int i = 0; i < jsonFilter.size(); i++) {
            JSONObject filterObj = jsonFilter.getJSONObject(i);
            //用来标记是否为外键字段，若值为[headIdProperty]则为标记字段
            String id = filterObj.getString("id");
            //用来标记外键字段在头结构中的属性名，例如头中的id字段
            String property = filterObj.getString("property");
            //用来标记外键字段在行结构中的值对应的属性名，例如行中的headId字段
            String value = filterObj.getString("value");
            //这里在前端定义死用headIdProperty标记行查询中的头id字段
            if ("headIdProperty".equals(id)) {
                //得到行查询的头部外键字段值
                Object foreignKeyVal = head.get(property);
                //将新的条件复写到filtersRaw
                filterObj.put("property", value);
                filterObj.put("value", foreignKeyVal);
                jsonFilter.remove(i);
                jsonFilter.add(filterObj);
                break;
            }
        }
        param.put("filtersRaw", jsonFilter.toJSONString());
        Set<String> keySet = param.keySet();
        Iterator<String> keySetIter = keySet.iterator();
        while (keySetIter.hasNext()) {
            String key = keySetIter.next();
            linkedMultiValueMap.add(key, param.get(key));
        }
        //拼接请求头
        HttpHeaders headers = new HttpHeaders();
        String auth = request.getHeader("Authorization");
        auth = auth.replace("bearer ", "");
        headers.setBearerAuth(auth);
        HttpEntity<Object> result = new HttpEntity<>(linkedMultiValueMap, headers);
        return result;
    }

    /**
     * 导出方法---将查询出的头行结构结果写入文件并返回url
     * @param headList
     * @return
     */
    private String writeExportFile(List<Map<String, Object>> headList) {
        KeyGenerateDao keyGenerateDao = (KeyGenerateDao) BeanTools.getBean(KeyGenerateDao.class);
        //初始化文件
        String fileName = keyGenerateDao.keyGenerate().toString() + ".txt";
        //初始化文件路径
        Map<String, String> paths = initPath(fileName);
        // 创建导出文件
        File exportFile = new File(paths.get("filePath"));
        exportFile.getParentFile().mkdirs();
        Boolean createNewFileFlag = null;
        try {
            createNewFileFlag = exportFile.createNewFile();
        } catch (IOException e) {
            throw new BasicServiceException("创建导出头行数据文件出现异常：" + e.getMessage());
        }
        //判断文件是否创建成功，记录日志
        if (createNewFileFlag) {
            LCLogger.warn().withMessageKey("writeExportFile").withMessage("writeExportFile: 文件创建成功").flush();
        } else {
            throw new BasicServiceException("创建导出头行数据文件出现异常失败，请稍后重试或联系管理员。");
        }
//        BufferedWriter bufferedWriter = null;
        try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(exportFile, true));) {
//            bufferedWriter = new BufferedWriter(new FileWriter(exportFile, true));
            bufferedWriter.write("[");
            for (int i = 0; i < headList.size(); i++) {
                Map<String, Object> obj = headList.get(i);
                JSONObject jsonObject = new JSONObject(obj);
                //将hashmap转为fastjson.jsonobject，格式化写入文件
                bufferedWriter.write(jsonObject.toJSONString());
                if (i != headList.size() - 1) {
                    bufferedWriter.write(",\n");
                }
            }
            bufferedWriter.write("]");
        } catch (IOException e) {
            LCLogger.error().withMessageKey("writeExportFile()").withMessage("将头行结果写入文件出现异常：" + e.getMessage()).flush();
            throw new BasicServiceException("将导出内容写入文件时出现异常，请稍后重试或联系管理员。");
        }

        return paths.get("urlPath");
    }

    /**
     * 导出方法---初始化文件路径及下载路径
     *
     * @author LiYang
     * @date 20191025
     * @param fileName
     * @return
     */
    private Map<String, String> initPath(String fileName) {
        Map<String, String> result = new HashMap<>(2);
        SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyyMMdd");
        String relatePath = "/export/" + dateFormatter.format(new Date()) + "/"
                + String.valueOf(System.currentTimeMillis()) + "/" + fileName;
        String filePath = CoreYmlConstants.getTemporaryIOFileDir() + relatePath;
        String urlPath = CoreYmlConstants.getTemporaryIOUrlDir() + relatePath;
        result.put("filePath", filePath);
        result.put("urlPath", urlPath);
        LCLogger.warn().withMessageKey("导出头行列表数据").withMessage("导出文件路径为：" + relatePath).flush();
        return result;
    }

    /**
     * 导入方法
     * 目前方法为一次性全部读取，如果考虑分批次读取可以考虑在导出时添加换行符/回车符，用于标记读取的批次
     *
     * @author LiYang
     * @date 20191025
     * @param entity
     * @param request
     * @return
     * @throws Exception
     */
    @Override
    public boolean importData(HeadRowStructure entity, HttpServletRequest request) {
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        MultipartFile myfile = multipartRequest.getFile("importFile");
        boolean result = false;
        if (null == myfile) {
            throw new BasicServiceException("未获取到上传的文件，请联系管理员。");
        } else {
            CommonsMultipartFile cmf = (CommonsMultipartFile) myfile;
            DiskFileItem dfi = (DiskFileItem) cmf.getFileItem();
            File file = dfi.getStoreLocation();
            result = readImportFile(file, entity, request);
        }
        return result;
    }

    public boolean readImportFile(File file, HeadRowStructure entity, HttpServletRequest request) {
//        BufferedReader reader = null;
        StringBuilder sb = new StringBuilder("");
        boolean result = false;
        try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
//            reader = new BufferedReader(new FileReader(file));
            String line = null;
            while (null != (line = reader.readLine())) {
                sb.append(line);
            }
        } catch (UnsupportedEncodingException e) {
            throw new BasicServiceException("导入文件编码格式转换异常，请联系管理员。");
        } catch (FileNotFoundException e) {
            throw new BasicServiceException("未找到导入文件，请联系管理员。");
        } catch (IOException e) {
            throw new BasicServiceException("读取文件时出现异常，请稍后重试或联系管理员。");
        }

        JSONArray arrayData = null;
        try {
            arrayData = JSONArray.parseArray(sb.toString());
        } catch (Exception e) {
            throw new BasicServiceException("导入文件的格式出现问题，请检查导入文件格式或重新导出。");
        }
        if (null == arrayData || arrayData.isEmpty()) {
            throw new BasicServiceException("导入文件内容为空，请检查导入文件格式或重新导出。");
        }
        KeyGenerateDao keyGenerateDao = (KeyGenerateDao) BeanTools.getBean(KeyGenerateDao.class);
        RestTemplate restTemplate = new RestTemplate();
        for (int i = 0; i < arrayData.size(); i++) {
            JSONObject record = arrayData.getJSONObject(i);
            JSONArray childrenArray = record.getJSONArray("exportChildren");
            //去掉exportChildren属性，用于调用头部插入
            record.remove("exportChildren");
            if ("Y".equalsIgnoreCase(entity.getNewIdFlag())) {
                Long uniqueKey = keyGenerateDao.keyGenerate();
                //重新设置头记录unique key(id)
                record.put(entity.getImpHeadPropName(), uniqueKey);
                //设置行的外键值
                for (int j = 0; j < childrenArray.size(); j++) {
                    JSONObject rowObj = childrenArray.getJSONObject(j);
                    //将指定的行记录外键的属性的值设为头记录unique key(id)
                    rowObj.put(entity.getImpRowForeignPropName(), uniqueKey);
                    rowObj.put("id", keyGenerateDao.keyGenerate());
                }
            }
            try {
                //得到头请求结果
                HttpEntity<Map<String, Object>> headRequestParam = repackageImpHeadParams(record, request);
                Map<String, Object> headResp = restTemplate.postForObject(entity.getImportHeadUrl(), headRequestParam, Map.class);
                //得到行请求结果
                HttpEntity<List<Map<String, Object>>> rowRequestParam = repackageImpRowParams(childrenArray, request);
                Map<String, Object> rowResp = restTemplate.postForObject(entity.getImportRowUrl(), rowRequestParam, Map.class);
                //判断插入头行的返回结果是否都为true
                result = "true".equals(headResp.get("success").toString()) && "true".equals(rowResp.get("success").toString());
            } catch (Exception e) {
                LCLogger.error().withMessageKey("readImportFile").withMessage("头行结构插入数据库时出现异常：" + e.getMessage()).flush();
                throw new BasicServiceException("插入数据时，出现异常。");
            }
        }
        return result;
    }

    /**
     * 导入方法---拼接头请求参数
     *
     * @author LiYang
     * @date 20191025
     * @param headObj
     * @param request
     * @return
     */
    private HttpEntity<Map<String, Object>> repackageImpHeadParams(JSONObject headObj, HttpServletRequest request) {
        //拼接请求头
        HttpHeaders headers = new HttpHeaders();
        String auth = request.getHeader("Authorization");
        auth = auth.replace("bearer ", "");
        headers.setBearerAuth(auth);
        headers.setContentType(MediaType.APPLICATION_JSON);
        //拼接请求体
        Map<String, Object> requestBody = (Map<String, Object>) headObj;
        //组成请求参数
        HttpEntity<Map<String, Object>> result = new HttpEntity<>(requestBody, headers);
        return result;
    }

    /**
     * 导入方法---拼接行请求参数
     *
     * @author LiYang
     * @date 20191025
     * @param childrenArray
     * @param request
     * @return
     */
    private HttpEntity<List<Map<String, Object>>> repackageImpRowParams(JSONArray childrenArray,
                                                                        HttpServletRequest request) {
        //拼接请求头
        HttpHeaders headers = new HttpHeaders();
        String auth = request.getHeader("Authorization");
        auth = auth.replace("bearer ", "");
        headers.setBearerAuth(auth);
        headers.setContentType(MediaType.APPLICATION_JSON);
        //拼接请求体
        List<Map<String, Object>> reqeustBody = (List) childrenArray.toJavaList(Map.class);
        //组成请求参数
        HttpEntity<List<Map<String, Object>>> result = new HttpEntity<List<Map<String, Object>>>(reqeustBody, headers);
        return result;
    }

    private String getDateTime() {
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return df.format(new Date());
    }
}
